package com.wfl.sampling.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.wfl.sampling.dao.UserDao;
import com.wfl.sampling.entity.*;
import com.wfl.sampling.entity.dto.UserDto;
import com.wfl.sampling.service.UserService;
import com.wfl.sampling.utils.HttpClientUtils;
import com.wfl.sampling.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.spec.AlgorithmParameterSpec;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @ Author     ：wfl
 * @ Date       ：Created in 10:20 2018/12/19
 * @ Description：
 * @ Modified By：
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {
    private final UserDao userDao;

    @Value("${wx.mini.appid}")
    private String appid;
    @Value("${wx.mini.secret}")
    private String secret;

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public User findByEmail(String email) {
        return userDao.findByEmail(email);
    }

    @Override
    public User findByPhone(String phone) {
        return userDao.findByPhone(phone);
    }

    @Override
    public int createUser(User user) {
        // 在这里为新用户设置最近的登录时间
        user.setLastLoginTime(LocalDateTime.now());

        // 然后插入这个新用户到数据库中
        return userDao.insertUser(user);
    }

    @Override
    public void updateLastLogin(int id) {
        userDao.updateLastLogin(id);
    }

    public User getUserById(int id) {
        return userDao.getUserById(id);
    }

    @Override
    public void saveUserInfoById(User user) {
        userDao.saveUserInfoById(user);
    }

    @Override
    public HashMap<String, ArrayList<User>> getUserRelationship(Integer id) {
        ArrayList<User> follower = userDao.selectFollower(id);
        ArrayList<User> followered = userDao.selectFollowered(id);
        //遍历查看是否互关
        Stream.concat(follower.stream(), followered.stream())
                .filter(user -> follower.stream().anyMatch(followerUser -> followerUser.getId() == user.getId())
                        && followered.stream().anyMatch(followedUser -> followedUser.getId() == user.getId()))
                .forEach(matchedUser -> matchedUser.setFollow(true));
        HashMap<String, ArrayList<User>> map = new HashMap<>();
        map.put("follower",follower);
        map.put("followered",followered);
        return map;
    }

    @Override
    public String followOnUser(Integer userId,Integer followUser) {
        try{
            userDao.savefollowOnUser(userId,followUser);
        } catch (Exception e) {
            log.info("关注失败",e);
            return "关注失败";
        }
        return "关注成功";
    }

    @Override
    public String followOffUser(Integer userId, Integer followUser) {
        try{
            userDao.deletefollowOffUser(userId,followUser);
        } catch (Exception e) {
            log.info("取关失败",e);
            return "取关失败";
        }
        return "取关成功";
    }

    @Override
    public String conversionBuilding(Integer userId, Integer otherId) {
        LocalDateTime currentTime = LocalDateTime.now();
        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 格式化当前时间
        String formattedTime = currentTime.format(formatter);
        try{
            Integer conversionId = userDao.selectConversion(userId, otherId);
            if(conversionId == null){
                userDao.saveConversion(userId,otherId,formattedTime);
            }
            else{
                return "会话创建失败,当前已有会话!";
            }
        } catch (Exception e) {
            log.info("会话创建失败",e);
            return "会话创建失败";
        }
        return "会话创建成功";
    }

    @Override
    public ArrayList<UserMessageDo> getUserMessage(Integer userId, Integer otherId) {
        //搜索会话id
        Integer conversionId = userDao.selectConversion(userId, otherId);
        if(conversionId == null){
            log.info("当前没有建立会话!");
            return null;
        }
        //根据会话id去找到对话信息
        ArrayList<UserMessageDo> userMessageDos = userDao.selectMessage(conversionId);
        return userMessageDos;

    }

    @Override
    public String saveConversionMessage(Integer userId, Integer otherId,String message) {
        //搜索会话id
        Integer conversionId = userDao.selectConversion(userId, otherId);
        if(conversionId == null){
           return "当前没有建立会话!";
        }
        LocalDateTime currentTime = LocalDateTime.now();
        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 格式化当前时间
        String formattedTime = currentTime.format(formatter);
        //根据会话id去找到对话信息
        userDao.saveConversionMessage(userId,conversionId,message);
        userDao.updateUserConversationTime(userId, otherId,formattedTime);
        return "对话保存成功";
    }

    @Override
    public List<MessageListDo> getMessageList(Integer userId) {
        return Stream.concat(userDao.selectMessageUserList(userId).stream(),userDao.selectMessageGroupList(userId).stream()).collect(Collectors.toList());
    }


    @Override
    public Result getSessionId(String code) {
        /*
        *   1.拼接一个url，微信登录凭证校验接口
        *   2.发起一个http的调用，获取微信的调用结果
        *   3.存不到redis中暂时还不会这个技术，所以我们存到数据库中,hhhh
        *   4.生成一个SessionId，返回给小程序前端，作为用户登录的标识
        *
        *
        * */
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
        String replaceUrl = url.replace("{0}", appid).replace("{1}", secret).replace("{2}", code);
        String res = HttpClientUtils.doGet(replaceUrl);
        String uuid = UUID.randomUUID().toString();
        //这里我们塞数据库里面，不会redis啊老铁，淦！
        userDao.insertSessionId(RedisKey.WX_SESSION_ID+uuid,res);
        Map<String,String> map = new HashMap<>();
        map.put("session_id",uuid);
        return Result.success(map);
    }

    @Override
    public Result getUserIdByOpenId(String sessionId) {
        if(sessionId==null)
            return null;
        User user = null;

            String json= userDao.selectSessionById(RedisKey.WX_SESSION_ID + sessionId);
            JSONObject jsonObject = JSON.parseObject(json);
            String openId = (String) jsonObject.get("openid");
            user = userDao.getUserByOpenId(openId);

        if(user==null)
            return null;
        return login(user);
    }

//    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result authLogin(WXAuth wxAuth){
        /*
        *
        * */
        try {
            String json = userDao.selectSessionById(RedisKey.WX_SESSION_ID+wxAuth.getSessionId());
            JSONObject jsonObject = JSON.parseObject(json);
            String sessionKey = (String) jsonObject.get("session_key");
            byte[] encData = cn.hutool.core.codec.Base64.decode(wxAuth.getEncryptedData());
            byte[] iv = cn.hutool.core.codec.Base64.decode(wxAuth.getIv());
            byte[] key = Base64.getDecoder().decode(sessionKey);
            AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keySpec = new SecretKeySpec(key,"AES");
            cipher.init(Cipher.DECRYPT_MODE,keySpec,ivSpec);
            String str = new String(cipher.doFinal(encData), StandardCharsets.UTF_8);
            WxUserInfo wxUserInfo = JSON.parseObject(str, WxUserInfo.class);
//            String openId = wxUserInfo.getOpenId();
            String openId = (String) jsonObject.get("openid");
            //这个地方出问题
//            int id = Integer.parseInt(openId);
            User user = null;
            user = userDao.getUserByOpenId(openId);
            if(user==null){
                //注册
//                UserDto userDto = new UserDto();
//                userDto.from(wxUserInfo);
                user = new User();
                user.from(wxUserInfo);
                user.setOpenId(openId);
                userDao.insertUser(user);
                return this.register(user);

            }
            else {
                //登录
                UserDto userDto = new UserDto();
                userDto.from(wxUserInfo);
                return this.login(user);
            }
        } catch (Exception e) {
            log.info("注册或者登录失败");
        }
        return Result.error();
    }

    @Override
    public Result register(User user) {
        //
//        User user= new User();
//        BeanUtils.copyProperties(userDto,user);
//        this.userDao.insertUser(user);
        return this.login(user);
    }

    @Override
    public Result login(User user) {
        Map<String,Object> claims = new HashMap<>();
        claims.put("id",user.getId());
        String token = JwtUtils.generateJwt(claims);
        user.setToken(token);
        user.setOpenId(null);
        return Result.success(user);
    }

    @Override
    public Result weChatLogin(User user) {
        try {
            String json = userDao.selectSessionById(RedisKey.WX_SESSION_ID+user.getSessionId());
            JSONObject jsonObject = JSON.parseObject(json);
            String openId = (String) jsonObject.get("openid");
            user.setOpenId(openId);
            userDao.insertUser(user);
            return this.register(user);
        } catch (Exception e) {
            log.info("用户注册失败！");
        }
        return Result.error();
    }

    public Result userInfo(String token,Boolean fresh){
        /*
        * 1.验证token
        * 2.refresh,true刷新，重新生成token
        * 3.false,直接返回用户信息*/
        try {
            Claims claims =JwtUtils.parseJwt(token);
            Integer id = (Integer) claims.get("id");
            User user = userDao.getUserById(id);
            return Result.success(user);
        }
        catch (Exception e){
            return Result.error("未登录");
        }
    }

}